Ext.ns('org.systemsbiology.carambola.containers.d3_graph');

console.log('org.systemsbiology.carambola.containers.d3_graph.D3GraphContainer! available at:' + org.systemsbiology.carambola.containers.d3_graph);

var D3GraphContainer = Ext.extend(Object, {
    currentState: {
        link_threshold: null
    },

    logo: {
       url: "https://carambola.googlecode.com/hg/src/images/logo.png", 
       label: "Carambola"
    },
    
    constructor: function(container) {
        console.log("org.systemsbiology.carambola.containers.D3GraphContainer(" + container + ")");
        Ext.apply(this, {contentEl:container});
    },

    d3obj: function() {
        var vis = d3.select("#chart")
    },

    draw: function(data, options) {
        console.log("org.systemsbiology.carambola.containers.D3GraphContainer(" + data + "," + options + ")");
        
        options.container = Ext.getDom(this.contentEl);
        var fill_color_scale = options.fill_color_scale === undefined ? ["red", "black"] : options.fill_color_scale;
        var link_stroke_width = options.link_stroke_width === undefined ? "1" : options.link_stroke_width;
        var num_nodes = options.num_nodes === undefined ? 100 : options.num_nodes;
        this.currentState.link_threshold = options.link_threshold === undefined ? 1.0 : options.link_threshold;

        var force;
        var link;
        var node;

        var all_nodes;
        var all_links;
        var link_degree;

        var filtered_nodes;

        var filtered_links_by_nodes;
        var filtered_links;

        var filter_links_by_max_nodes = function(p_raw_links, max_nodes, p_nodes) {
            filtered_nodes = Array();
            filtered_links_by_nodes = Array();
            
            var last_idx = p_raw_links.length / 3;
            
            for (l = 0; l < last_idx; l++) {
                var source = p_raw_links[3 * l];
                var target = p_raw_links[3 * l + 1];
                var link_value = p_raw_links[3 * l + 2];

                if (source > (max_nodes - 1)) {
                    continue;
                }

                if (target > (max_nodes - 1)) {
                    continue;
                }
                
                filtered_links_by_nodes.push(l);
            }

            for (n = 0; n < max_nodes; n++) {
                filtered_nodes[n] = p_nodes[n];
            }
        };
        
        var filter_links_by_threshold = function(p_raw_links, threshold) {
            filtered_links = Array();
        
            var last_idx = undefined;

            for(i = 0; i < filtered_links_by_nodes.length; i++) {
                 idx = filtered_links_by_nodes[i];                
                 if (p_raw_links[3*idx+2] < threshold) {
                     break;
                 }

                 current_value = p_raw_links[3*idx+2];

                 filtered_links.push({
                     source: p_raw_links[3*idx],
                     target: p_raw_links[3*idx+1],
                     value: current_value
                 });
                
                 last_idx = idx;
            }
        };

        var restart = function(current_threshold) {
            var node_fill = d3.scale.linear().domain([0, filtered_nodes.length]).range(fill_color_scale);
            var link_fill = d3.scale.log()
                                .domain([current_threshold, filtered_links[0].value])
                                .range([fill_color_scale[1], fill_color_scale[0]]);

            var max_link_val = filtered_links[0].value;

            var link_fill_by_endpoint = function(d) {
               return fill(d3.min([d.source, d.target]));
            };
            
            var link_fill_by_value = function(d) {
                return link_fill(d.value);
            };

            link = vis.selectAll("line.link")
                .data(filtered_links)
                .enter().append("svg:line")
                .attr("class", "link")
                .style("stroke", link_fill_by_value)
                .style("stroke-width", link_stroke_width)
                .attr("x1", function(d) { return d.source.x; })
                .attr("y1", function(d) { return d.source.y; })
                .attr("x2", function(d) { return d.target.x; })
                .attr("y2", function(d) { return d.target.y; });

            node = vis.selectAll("circle.node")
                .data(filtered_nodes)
                .enter().append("svg:circle")
                .attr("class", "node")
                .attr("cx", function(d) { return d.x; })
                .attr("cy", function(d) { return d.y; })
                .attr("r", 5)
                .style("fill", function(d, i) { return node_fill(i); })
                .call(force.drag);

            node.append("svg:title")
                .text(function(d) { return d.name; });
            
            force.start();
        };
        
        var loadFn = function(o) {
            var json = Ext.util.JSON.decode(o.responseText);
            all_nodes = json.nodes;
            all_links = json.links;

            filter_links_by_max_nodes(all_links, num_nodes, all_nodes);
            filter_links_by_threshold(all_links, this.currentState.link_threshold);

            vis = d3.select(options.container).insert("svg:svg")
                .attr("width", options.width)
                .attr("height", options.height);

            force = d3.layout.force()
                .nodes(filtered_nodes)
                .links(filtered_links)
                .size([options.width, options.height]);

            force.on("tick", function() {
                vis.selectAll("line.link")
                    .attr("x1", function(d) { return d.source.x; })
                    .attr("y1", function(d) { return d.source.y; })
                    .attr("x2", function(d) { return d.target.x; })
                    .attr("y2", function(d) { return d.target.y; });
                
                vis.selectAll("circle.node")
                    .attr("cx", function(d) { return d.x; })
                    .attr("cy", function(d) { return d.y; });
            });

            restart(this.currentState.link_threshold);
        };

        Ext.Ajax.request({ url: data.uri, method: "get", success: loadFn, scope: this});
    },

    GetState: function() {
        console.log("org.systemsbiology.carambola.containers.D3GraphContainer.GetState(): TODO: gather state from contained visualization");
        return this.currentState;
    },
    
    SetState: function(state) {
        this.currentState = state;
        console.log("org.systemsbiology.carambola.containers.D3GraphContainer.SetState(" + state + "): TODO: pass state on to contained visualization");
    }
});

org.systemsbiology.carambola.containers.d3_graph.Container = D3GraphContainer;

org.systemsbiology.pages.apis.events.MessageBus.fireEvent("publish", {
    key: "org.systemsbiology.carambola.containers.d3_graph.Container",
    payload: org.systemsbiology.carambola.containers.d3_graph.Container
});
